/*
 * LockedNumber.cpp
 *
 *  Created on:  Jun 12, 2009
 *      Author:  Zach Riggle A37627537
 *      Project: CSE410 Project 2
 */

#include <pthread.h>
#include <semaphore.h>
#include <sys/mman.h>
#include <sys/errno.h>
#include "Log.h"
#include "LockedNumber.h"

int printMutexChange(int n) {
    switch (n) {
    case 0:
        break;
    case EINVAL:
        logmutex("Invalid mutex value/not initialized");
        break;
    case EPERM:
        logmutex("Cannot unlock, owned by another thread.");
        break;
    case EBUSY:
        logmutex("Already locked.");
        break;
    case EFAULT:
        logmutex("Invalid pointer.");
        break;
    case EDEADLK:
        logmutex("A deadlock would occur if locked.");
        break;
    default:
        logmutex("Unknown error: " << n);
    }
    return n;
}

LockedNumber::LockedNumber() {
    lockCount = semCount = 0;

    // Map the memory.
    data = (int *) mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED
                        | MAP_ANON, -1, 0);
    *data = 0;

    lockAttr = (pthread_mutexattr_t*) mmap(NULL, sizeof(pthread_mutexattr_t), PROT_READ|PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
    lock = (pthread_mutex_t*) mmap(NULL, sizeof(pthread_t), PROT_READ|PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);

    // Set up the lock, make it recursive and shared.
    pthread_mutexattr_init(lockAttr);
    pthread_mutexattr_setpshared(lockAttr, PTHREAD_PROCESS_SHARED);
    pthread_mutexattr_settype(lockAttr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(lock, lockAttr);

    // Set up the sempahore
//	sem_init(&sem, 1, 1);
}

LockedNumber::~LockedNumber() {
//	sem_close(&sem);

    munmap((void*)data, sizeof(int));
    munmap(lockAttr, sizeof(pthread_mutexattr_t));
    munmap(lock, sizeof(pthread_mutex_t));

    pthread_mutexattr_destroy(lockAttr);
    pthread_mutex_destroy(lock);
}

int LockedNumber::tryLock() {
    int ret = 0;

//	ret = sem_trywait(&sem);
//	if (ret == 0)
//		semCount++;
//	else
//		return ret;

    ret = printMutexChange(pthread_mutex_trylock(lock));
    if (ret == 0)
        lockCount++;
    logmutex("Lock count is " << lockCount);
    return ret;
}

int LockedNumber::unlock() {
    lockCount--;
    logmutex("Lock count is " << lockCount);
    return printMutexChange(pthread_mutex_unlock(lock));
}

int LockedNumber::get() {
    printMutexChange(pthread_mutex_lock(lock));
    lockCount++;

    int retVal = *data;

    lockCount--;
    printMutexChange(pthread_mutex_unlock(lock));
    logmutex("Lock count is " << lockCount);
    return retVal;
}

void LockedNumber::set(int n) {
    printMutexChange(pthread_mutex_lock(lock));
    lockCount++;
    *data = n;

    lockCount--;
    printMutexChange(pthread_mutex_unlock(lock));
    logmutex("Lock count is " << lockCount);
}
